/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ImportDeclaration, ExportDeclaration, SourceFile } from 'ts-morph';
import { ITaskContext, Logger } from '../..';
import { GeneratedFileInfo, ImportTransformService } from '../types/ImportRewriteTypes';
import { ImportScanStrategyImpl } from '../strategies/ImportScanStrategy';
import SerializationPathUtil from '../../utils/SerializationPathUtil';
import { ErrorCodes } from '../../utils/CustomError';
import { TsMorphUtil } from '../../utils/TsMorphUtil';

/**
 * 导入转换服务实现
 * 基于 ts-morph 的导入语句重写服务
 */
export class ImportTransformServiceImpl implements ImportTransformService {
  private readonly scanStrategy: ImportScanStrategyImpl;

  constructor() {
    this.scanStrategy = new ImportScanStrategyImpl();
  }

  rewriteFileImports(
    filePath: string,
    context: ITaskContext,
    generatedFileInfos: GeneratedFileInfo[],
    preserveOriginalImports: boolean = false
  ): void {
    try {
      const sourceFile = TsMorphUtil.getProject().addSourceFileAtPath(filePath);

      const importRewriteCount = this.processImportDeclarations(
        sourceFile,
        context,
        generatedFileInfos,
        preserveOriginalImports
      );

      const exportRewriteCount = this.processExportDeclarations(
        sourceFile,
        context,
        generatedFileInfos,
        preserveOriginalImports
      );

      const totalRewriteCount = importRewriteCount + exportRewriteCount;
      this.handleRewriteResult(sourceFile, filePath, totalRewriteCount);
    } catch (error) {
      this.handleRewriteError(error, filePath);
      throw error;
    }
  }

  private processImportDeclarations(
    sourceFile: SourceFile,
    context: ITaskContext,
    generatedFileInfos: GeneratedFileInfo[],
    preserveOriginalImports: boolean
  ): number {
    const importDeclarations = sourceFile.getImportDeclarations();
    let rewriteCount = 0;

    for (const importDecl of importDeclarations) {
      // 传递 context 到扫描策略，以支持模块化导入处理
      const matchedInfo = this.scanStrategy.shouldRewriteImport(
        importDecl,
        sourceFile.getFilePath(),
        generatedFileInfos,
        context
      );

      if (matchedInfo) {
        this.rewriteSingleImport(importDecl, context, matchedInfo, preserveOriginalImports);
        rewriteCount++;
      }
    }

    return rewriteCount;
  }

  private processExportDeclarations(
    sourceFile: SourceFile,
    context: ITaskContext,
    generatedFileInfos: GeneratedFileInfo[],
    preserveOriginalImports: boolean
  ): number {
    const exportDeclarations = sourceFile.getExportDeclarations();
    let rewriteCount = 0;

    for (const exportDecl of exportDeclarations) {
      const moduleSpecifier = exportDecl.getModuleSpecifierValue();

      // 没有 moduleSpecifier 说明没有 from 子句，跳过
      if (!moduleSpecifier) {
        continue;
      }

      // 使用相同的扫描策略检查导出
      const matchedInfo = this.scanStrategy.shouldRewriteExport(
        moduleSpecifier,
        sourceFile.getFilePath(),
        generatedFileInfos,
        context
      );

      if (matchedInfo) {
        this.rewriteSingleExport(exportDecl, context, matchedInfo, preserveOriginalImports);
        rewriteCount++;
      }
    }

    return rewriteCount;
  }

  private rewriteSingleExport(
    exportDecl: ExportDeclaration,
    context: ITaskContext,
    matchedInfo: GeneratedFileInfo,
    preserveOriginalImports: boolean
  ): void {
    const originalModuleSpecifier = exportDecl.getModuleSpecifierValue();
    if (!originalModuleSpecifier) {
      // 如果没有 moduleSpecifier，说明没有 from 子句，不应该到这里
      return;
    }

    const newModuleSpecifier = SerializationPathUtil.calculateModularImportPath(matchedInfo.generatedFilePath, context);

    if (preserveOriginalImports) {
      this.replaceWithCommentedOriginal(exportDecl, originalModuleSpecifier, newModuleSpecifier);
    } else {
      exportDecl.setModuleSpecifier(newModuleSpecifier);
    }
  }

  private rewriteSingleImport(
    importDecl: ImportDeclaration,
    context: ITaskContext,
    matchedInfo: GeneratedFileInfo,
    preserveOriginalImports: boolean
  ): void {
    const originalModuleSpecifier = importDecl.getModuleSpecifierValue();
    const newModuleSpecifier = SerializationPathUtil.calculateModularImportPath(matchedInfo.generatedFilePath, context);

    if (preserveOriginalImports) {
      this.replaceWithCommentedOriginal(importDecl, originalModuleSpecifier, newModuleSpecifier);
    } else {
      importDecl.setModuleSpecifier(newModuleSpecifier);
    }
  }

  private replaceWithCommentedOriginal(
    importDecl: any,
    originalModuleSpecifier: string,
    newModuleSpecifier: string
  ): void {
    const originalImportText = importDecl.getFullText().trim();
    const commentedOriginal = `// ${originalImportText}`;

    const newImportText = importDecl
      .getFullText()
      .replace(new RegExp(`(['"]?)${this.escapeRegExp(originalModuleSpecifier)}\\1`, 'g'), `'${newModuleSpecifier}'`);

    importDecl.replaceWithText(`${commentedOriginal}\n${newImportText.trim()}`);
  }

  private handleRewriteResult(sourceFile: any, filePath: string, rewriteCount: number): void {
    if (rewriteCount > 0) {
      sourceFile.saveSync();
      Logger.info(`文件 ${filePath} 重写了 ${rewriteCount} 个导入语句`);
    }
    sourceFile.forget();
  }

  private handleRewriteError(error: unknown, filePath: string): void {
    if (error instanceof Error) {
      Logger.error(`Error: ${error.constructor.name}: ${ErrorCodes.REWRITE_IMPORT_FAIL}`);
      Logger.error(`Error Message: 重写文件导入失败: ${filePath}`);
      Logger.error(`错误信息: ${error.message}\n${error.stack}`);
    } else {
      Logger.error(`重写文件导入失败: ${filePath}, 未知错误: ${error}`);
    }
  }

  private escapeRegExp(string: string): string {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  }
}
